package org.trc.trajectory.analysis;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;

import org.geotools.data.shapefile.ShpFiles;
import org.geotools.data.shapefile.dbf.DbaseFileReader;
import org.geotools.data.shapefile.shp.ShapefileException;
import org.geotools.data.shapefile.shp.ShapefileReader;
import org.trc.network.Edge;
import org.trc.network.Network;
import org.trc.network.Node;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;

/**
 * 
 * @author Lin Xu
 *
 */

public class RouteStatistics {
	Network network = null;
	static final String MR = "7-9";
	static final String ER = "5-7";
	static final String DAY = "24";
	
	public RouteStatistics(Network network){
		this.network = network;
	}
	
    /**
     * 获取相邻路段的节点ID
     * 
     * @param preEdgeID
     * @param sucEedgeID
     * ruturn a transitionID
     */
	private String calTurnNode(String preEdgeID, String sucEdgeID){
		Edge edge1 = this.network.getEdgeById(preEdgeID);
		Edge edge2 = this.network.getEdgeById(sucEdgeID);
		if(edge1 == null || edge2 == null){
			System.out.println("we can not find edge");
			return null;
		}
		String[] nodeSQ = new String[] {edge1.getStartNodeId(), edge1.getEndNodeId(), edge2.getStartNodeId(), edge2.getEndNodeId(),};
		if(nodeSQ[0].equals(nodeSQ[2])|| nodeSQ[0].equals(nodeSQ[3])){
			//System.out.println("Via node: " + nodeSQ[0]);
			return nodeSQ[0];
		}else if (nodeSQ[1].equals(nodeSQ[2])|| nodeSQ[1].equals(nodeSQ[3])){
			//System.out.println("Via node: " + nodeSQ[1]);
			return nodeSQ[1];
		}else
			//System.out.println("Note: There  is  no  Crosing  Node!!!");
			return null;
	}

	/**
	 * 计算单台车的浮动车轨迹数据
	 * 
	 * @param fcdShapeURL
	 * @throws ShapefileException 
	 * @throws IOException 
	 */
	public void historyMovementStatistic(File fcdShapeFile, String timeRange, String type) throws IOException, ShapefileException{
        //读取几何部分
		ShapefileReader fcdShapeReader = new ShapefileReader(new ShpFiles(fcdShapeFile), false, false, new GeometryFactory());
        MultiLineString trj = null; // Trip (连续路段相连)
        LineString trjSegment1 = null; 
        LineString trjSegment2 = null; // 相邻轨迹段（以路段为单位）
        String roadSegmentId1 = null;
        String roadSegmentId2 = null; // 相邻路段
        String adjNodeId = null; //转向点
        //读取属性部分
        String fcdAttrFile = fcdShapeFile.getPath().split("\\.")[0]+  ".dbf";
        DbaseFileReader attrReader = new DbaseFileReader(new ShpFiles(fcdAttrFile), false, Charset.forName("US-ASCII"));
        int startTime = 0;
        int endTime = 0;
        int j = 1;
        while (fcdShapeReader.hasNext()) {
        	System.out.println(fcdShapeFile.getPath().split("\\.")[0] + " Traj: " + j++ ); //正在读取某车的第J条轨迹
        	trj = (MultiLineString) fcdShapeReader.nextRecord().shape();
        	attrReader.read();
        	startTime = ((Double)attrReader.readField(0)).intValue();
        	endTime = ((Double)attrReader.readField(1)).intValue();
        	if (timeRange.equals(MR)){//早高峰
        		if(startTime>32400||endTime < 25200){
        			continue;
        		}
        	}else if (timeRange.equals(ER)){//晚高峰
        		if(startTime>68400||endTime < 61200){
        			continue;
        		}
        	}
        	if(type.equals("TURN"))//转向信息统计
        		this.nodeTransitionStatistic(trj);
        	else if(type.equals("TRANSFER")) //路径信息统计
        		this.edgeTransferStatistic(trj);
        	else if(type.equals("ROUTEPRE")) //路径预测统计
        		this.routePredictStatistic(trj);
        	else
        		break;
        }
        
        fcdShapeReader.close();
        attrReader.close();
	} 
	
	/**
	 * 计算轨迹段中的转向信息
	 * 
	 * @param trj
	 */
	private void nodeTransitionStatistic(MultiLineString trj){
        LineString trjSegment1 = null; // 相邻轨迹段（以路段为单位）
        LineString trjSegment2 = null; 
        String roadSegmentId1 = null; // 相邻路段
        String roadSegmentId2 = null; 
        String adjNodeId = null; //转向点
        for (int i = 0; i < trj.getNumGeometries()- 1; i++) {
        	trjSegment1 = (LineString) trj.getGeometryN(i);
        	trjSegment2 = (LineString) trj.getGeometryN(i+1);
//        	roadSegmentId1 = "4554" + Integer.toString((int)trjSegment1.getCoordinateN(0).z).substring(2);
//        	roadSegmentId2 = "4554" + Integer.toString((int)trjSegment2.getCoordinateN(0).z).substring(2);	
        	roadSegmentId1 = Integer.toString((int)trjSegment1.getCoordinateN(0).z);
        	roadSegmentId2 = Integer.toString((int)trjSegment2.getCoordinateN(0).z);	
        	adjNodeId = this.calTurnNode(roadSegmentId1, roadSegmentId2);
        	if(adjNodeId != null)
        		this.network.getNodeById(adjNodeId).addOneTurn(roadSegmentId1, roadSegmentId2);
        }
	}
	
	/**
	 * 计算轨迹段中的频次信息
	 * 
	 * @param trj
	 */
	private void edgeTransferStatistic(MultiLineString trj){
        LineString trjSegment = null; // 轨迹段（以路段为单位）
        String roadSegmentId = null; // 路段
        String orginString = null;
        for (int i = 0; i < trj.getNumGeometries()- 1; i++) {
        	trjSegment = (LineString) trj.getGeometryN(i);
        	orginString = Integer.toString((int)trjSegment.getCoordinateN(0).z);
        	if(orginString.substring(0, 2).equals("36"))
        		roadSegmentId = "4554" + orginString.substring(2);
        	else if(orginString.substring(0, 2).equals("46"))
        		roadSegmentId = "4654" + orginString.substring(2);
        	else {
        		System.out.println("There is a problem when calculating the trj");
        		return;
        	}       		
        	if(roadSegmentId != null)
        		this.network.getEdgeById(roadSegmentId).count++;
        }
	}

	/**
	 * 基于贪婪算法的路径预测准确性统计
	 * 
	 * @param trj
	 */
	private void routePredictStatistic(MultiLineString trj){
		ArrayList<String> actRouteEdgeList = new  ArrayList<String>();
		String[] actRouteEdgeIds = null;
		String[] preRouteEdgeIds = null;
		
        LineString trjSegment1 = null; 
        LineString trjSegment2 = null; 
        String roadSegmentId1 = null; //相邻轨迹段
        String roadSegmentId2 = null; 
        String adjNodeId = null;
        
    	trjSegment1 = (LineString) trj.getGeometryN(0);
    	trjSegment2 = (LineString) trj.getGeometryN(1);
    	roadSegmentId1 = Integer.toString((int)trjSegment1.getCoordinateN(0).z);
    	roadSegmentId2 = Integer.toString((int)trjSegment2.getCoordinateN(0).z);
    	adjNodeId = this.calTurnNode(roadSegmentId1, roadSegmentId2);
    	preRouteEdgeIds = this.routePredict(roadSegmentId1, adjNodeId, trj.getNumGeometries());
        
        for (int i = 0; i < trj.getNumGeometries(); i++) {
        	trjSegment1 = (LineString) trj.getGeometryN(i);
        	roadSegmentId1 = Integer.toString((int)trjSegment1.getCoordinateN(0).z);
        	actRouteEdgeList.add(roadSegmentId1);
        }
		
        actRouteEdgeIds = actRouteEdgeList.toArray(new String[]{});
        
        System.out.println(this.calPathSimilarityF1(preRouteEdgeIds, actRouteEdgeIds));
        
	}
	
	private String[] routePredict(String startEdgeId, String nextNodeId, int maxSeg){
		ArrayList<String> preRouteEdgeList = new  ArrayList<String>();
		while(maxSeg>0){
			preRouteEdgeList.add(startEdgeId);
			maxSeg--;
			startEdgeId = this.network.getNodeById(nextNodeId).getMaxProTurnEdgeId(startEdgeId);
			if(startEdgeId == null)
				break;
			nextNodeId =  this.network.getEdgeById(startEdgeId).getAnotherNodeId(nextNodeId);
		}
		return preRouteEdgeList.toArray(new String[]{});
	}
	
	private float calPathSimilarityF1(String[] preRouteEdgeIds, String[] actRouteEdgeIds){
		float precision = (float)this.shareRoadSegmentNum(preRouteEdgeIds,actRouteEdgeIds)/(float)preRouteEdgeIds.length;
	    float recall = (float)this.shareRoadSegmentNum(preRouteEdgeIds,actRouteEdgeIds)/(float)actRouteEdgeIds.length;
		return 2 * precision * recall/(precision + recall);
	}
	
	private int shareRoadSegmentNum(String[] preEdgeList, String[] actEdgeList){
		int num = 0;
		for(int i = 0; i < preEdgeList.length; i++){
			for(int j = 0; j < actEdgeList.length; j++){
				if(preEdgeList[i].equals(actEdgeList[j]))
					num ++;		
			}
		}
		return num;
	}
	
    /**
     * 浮动车历史运动统计
     * 
     * @param fcdShapeFileDirURL
     * @param timeRange
     * @param type "TURN" 转向统计  "Transfer" 路段频次统计
     * @throws IOException
     * @throws ShapefileException
     */
	public void historyMovementStatistic(String fcdShapeFileDirURL, String timeRange, String type) throws IOException, ShapefileException{
		File[] files = new File(fcdShapeFileDirURL).listFiles(shapeFileFilter);
		int i = 1;
		for(File file : files){
			System.out.println(i++);
			//if(i == 50) break;
			this.historyMovementStatistic(file,timeRange, type);
		}
	}
	
	//-----------------------------------------------------------------------------------//
	
	/**
	 * 将交叉口的转向统计导入数据库
	 * 
	 * @param tableName
	 * @param con
	 * @throws SQLException 
	 */
	public void exportTranstionStatistic(String tableName, Connection con) throws SQLException{
		//SQL准备
		String insertSQL = "INSERT INTO " + tableName + "(NODEID, FROMEDGEID, TOEDGEID, SUPPORT, CONFIDENCE, PERIOD, TURNTYPE)"
		+ " VALUES(?, ?, ?, ?, ?, ?, ?)";
		PreparedStatement insertState = con.prepareStatement(insertSQL);
		//节点遍历
		for(Node node : this.network.nodeCollection.values()){
			if(node.turnCount == 0 || node.numOfEdge <= 2) //节点的重要性
				continue;
			String[] adjEdges = node.getAdjEdges();
			for (int i = 0; i < adjEdges.length; i++){
				for(int j = 0; j < adjEdges.length; j++ ){
					int turnCount = node.getTurnCount(adjEdges[i], adjEdges[j]);
					if (turnCount == 0) //转向重要性
						continue;
					float turnProbility = node.getTurnProbility(adjEdges[i], adjEdges[j]);
					//TODO: String direct = this.network.getTurnDirection(adjEdges[i], adjEdges[j]);
					insertState.setString(1, node.id); // NodeId
					insertState.setString(2, adjEdges[i]); // FromEdge
					insertState.setString(3, adjEdges[j]); // ToEdge
					insertState.setDouble(4, turnCount); // support
					insertState.setDouble(5, turnProbility); // confidence
					//insertState.setString(6, "Day"); // period
					insertState.setString(6, MR); // period
					insertState.setString(7, null); // turntype	
					insertState.executeUpdate();
				}
			}
		}
	}
	
	/**
	 * 将道路频次信息导入数据库
	 * 
	 * @param tableName
	 * @param con
	 * @throws SQLException 
	 */
    public void exportTransferStatistic(String tableName, Connection con) throws SQLException{
		//SQL准备
    	String updateSQL = "UPDATE " + tableName + " SET count = ? WHERE id = ?";
    	PreparedStatement updateState = con.prepareStatement(updateSQL);
    	
    	//边遍历
		for(Edge edge : this.network.edgeCollection.values()){
			if(edge.count == 0) 
				continue;
			updateState.setInt(1, edge.count);
			updateState.setString(2, edge.id);
			updateState.executeUpdate();
		}
    }
    
	private static FileFilter shapeFileFilter = new FileFilter() {
		@Override
		public boolean accept(File pathname) {
			return pathname.toString().endsWith(".shp");
		}
	};
	
	public static void main(String[] args) throws IOException, ShapefileException, SQLException, ClassNotFoundException {
/*		DBReaderWriter dp = new DBReaderWriter();
		//Network network = new Network(dp.getNetworkFileURL(),null);
		//Network network = new Network("D:\\Data\\lyg\\RWuhan45_polyline",null);
		Network network = new Network("D:\\Data\\tw\\RNWuhan",null);
		network.initTurnMatrixByDB("transition", dp.getConnection("FCD"));
		RouteStatistics rs = new RouteStatistics(network);
		rs.historyMovementStatistic("D:\\Data\\fcdsample", "24", "ROUTEPRE");*/
//		String shapeFileURL = "D:\\Lin Xu\\My Documents\\GISDataBase\\FCD\\Taxi_Tuwei\\taxi\\Taxi Trajectory\\11202.shp";
		//rs.historyMovementStatistic(dp.getFcdDataFileDir(), "24", "TRANSFER");
		//rs.exportTransferStatistic("rwuhan", dp.getConnection("FCD"));
//		rs.exportTranstionStatistic("transition", dp.getConnection("FCD"));
//		for(Node node : network.nodeCollection.values()){
//			if(node.turnCount != 0 && node.numOfEdge > 3)
//			System.out.println(node.id + ":" + node.turnCount);
//		}
	}

}
